package com.wfs.aqs.lock;

import sun.misc.Unsafe;

import java.lang.reflect.Field;
import java.util.concurrent.locks.LockSupport;

/**
 * @author wfs
 * @date 2022-01-16 21:47
 */
public class MiniReentrantLock implements Lock {
    /**
     * 加锁状态
     * 0 未加锁
     * >0 加锁状态
     */
    private volatile int state;

    /**
     * 独占模式
     *  同一时刻 只有一个线程可以持有锁。 其他线程在获取到锁是，会被阻塞
     *
     */
    //当前独占锁的线程
    private Thread exclusiveOwnerThread;
    /**
     * 阻塞队列头尾节点
     */
    private Node head; //head节点对应的线程就是当前占用锁的线程 === exclusiveOwnerThread
    private Node tail;

    /**
     * 阻塞的线程被封装成 node 节点
     * 然后放到FIFO队列
     */
    static final class Node{
        //前置节点
        Node prev;
        //后置节点
        Node next;
        //封装的线程
        Thread thread;
        public Node(Thread thread){this.thread=thread;}
        public Node(){

        }

    }

    public int getState() {
        return state;
    }

    /**
     * 加锁方法
     * 模拟公平锁
     * 场景分析：
     * 场景1：线程进来后发现，当前state==0,这个时候直接获取到锁
     * 场景2：线程进来后发现，当前state>0 ,当前线程入队
     */
    @Override
    public void lock() {
        //第一次获取到锁时将 state设置为1
        //第n重入时，将state设置为n
        acquire(1);
    }


    /**
     * 竞争
     * 1.尝试获取锁，成功则占用锁
     * 2.抢占锁失败，阻塞当前线程
     */
    private void acquire(int i) {
        if (!tryAcquire(i)){
            //占用锁失败
            Node node=addWaiter();
            acquireQueued(node,i);
        }

    }

    /**
     * 尝试抢占锁失败需要
     * 1.需要将当前线程封装成node 加入到阻塞对垒
     * 2.需要将当前线程park掉，使线程处于挂起状态
     *
     * //唤醒后：
     * 1.检查当前node节点 是否为head.next节点
     *   head.next节点是拥有占用权限的线程，其他的node无抢占的权限
     * 2.抢占
     *  成功：1.将当前的node设置为head ,将老的head出队操作，返回业务层面
     *  失败：2.即系park，等待唤醒
     *
     *  ===》
     *  1.添加阻塞队列 addWorker
     *  2.竞争资源
     */
    private void acquireQueued(Node node, int i) {
        for (;;){
            //当前node被唤醒之后可以尝试去获取锁呢？
            //只有一种情况，当前node是 head 的后继节点。才有这个权限。
            //head 节点 就是 当前持锁节点..
            Node pred=node.prev;
            if (pred==head && tryAcquire(i)){
                //说明线程竞争锁成功
                this.head=node;
                //??? 为什么将线程设置为null, 因为当前node已经是获取锁成功的线程
                node.thread=null;
                node.prev=null;

                pred.next=null;
                return;
            }
            System.out.println("线程：" + Thread.currentThread().getName() + "，挂起！");
            //将当前线程挂起！
            LockSupport.park();
            System.out.println("线程：" + Thread.currentThread().getName() + "，唤醒！");

            //什么时候唤醒被park的线程呢？
            //unlock 过程了！
        }


    }

    private Node addWaiter() {
        Node newNode = new Node(Thread.currentThread());
        //如何入队
        //1.找到newNode 的前置节点pred
        //2.更新newNode.prev=pred
        //3.CAS更新tail 为newNode
        //4.更新pred.next=newNode
        //前置条件：队列已经有等待者node了，当前node不是第一个入队的node
        Node pred=tail;
        if (pred!=null){
            newNode.prev=pred;
            if (compareAndSetTail(pred,newNode)){
                pred.next=newNode;
                return newNode;
            }
        }
        //执行到这几种情况
        //1.tail == null 队列为空
        //2.cas 设置当前newNode为tail是失败
        enq(newNode);
        return newNode;
    }

    /**
     * 自旋入队 只有成功后才返回
     *
     * 1.tail == null 队列为空
     * 2.cas被其他线程抢先了一步
     */
    private void enq(Node newNode) {
        for (;;){
            if (tail==null){
                if (compareAndSetHead(new Node())){
                    tail=head;
                    //这里不直接返回，还会继续自旋
                }
            }else {
                Node pred=tail;
                if (pred!=null){
                    newNode.prev=pred;
                    if (compareAndSetTail(pred,newNode)){
                        pred.next=newNode;
                        //入队成功结束
                        return;
                    }
                }

            }
        }
    }

    /**
     * 尝试获取锁
     * @return true 获取锁成功   false 获取锁失败
     */
    private boolean tryAcquire(int i) {
        if (state==0){
            //当前state==0，是否可以直接抢占到锁？
            //不一定，因为模式的是公平锁，先来后到
            //条件1：!hasQueuedPredecessor() 表示当前线程前没有等待者 线程
            //条件2：compareAndSetState(0, i) 成立说明抢占到锁。
            if (!hasQueuedPredecessor() && compareAndSetState(0,i)){
                this.exclusiveOwnerThread=Thread.currentThread();
                return true;
            }
        }else if (Thread.currentThread() == this.exclusiveOwnerThread){
            //加锁线程和当前锁执行线程为同一个， 支持重入. 不存在并发
            int c=getState();
            c=c+i;
            // ?
            this.state=c;
            return true;

        }
        //执行到此处的情况
        //1.CAS失败
        //2.state>0 且当前线程不是占有线程
        return false;
    }


    /**
     * 判断阻塞队列
     * true ->表示当前线程前面有等待者线程
     * false->当前线程没有其他等待者线程
     *
     * 调用链
     * lock->acquire->tryAcquire->hasQueuedPredecessor
     * 返回false
     * 1.当前队列为空
     * 2.当前线程为head.next节点线程
     */

    private boolean hasQueuedPredecessor() {
        Node h=head;
        Node t=tail;
        Node s;
        //条件1：h!=t
        //成立：说明当前队列已经有node了
        //不成立：1.h==t == null 2.h==t==head 第一个获取锁失败的线程，会为当前持有锁的线程补充创建一个head节点

        //条件2：前置条件 条件1一定成立
        //排除2.1 s = h.next) == null
        //条件2.2：前置条件h.next不为空 s.thread != Thread.currentThread()
        //条件成立: 说明当前线程就不是h.next线程，返回true
        //条件不成立：说明当前线程，就是h.next线程返回false
        return h != t && ((s = h.next) == null || s.thread != Thread.currentThread());
    }

    /**
     * 释放锁
     */
    @Override
    public void unlock() {
        release(1);
    }


    private void release(int i) {
        if (tryRelease(i)){
            Node head = this.head;
            //条件成立 说明有等待者
            if (head.next!=null){
                //公平锁，就是唤醒head.next节点
                unparkSuccessor(head);
            }
        }
    }

    private void unparkSuccessor(Node node) {
        Node s=node.next;
        if (s!=null && s.thread!=null){
            LockSupport.unpark(s.thread);
        }
    }

    /**
     * 尝试释放锁
     * 成功 返回true
     * 否则说明当前state>0 返回false
     */
    private boolean tryRelease(int i) {
        int c=getState()-i;
        if (this.exclusiveOwnerThread !=Thread.currentThread()){
            throw new RuntimeException("线程持有锁才可释放");
        }
        //执行到这里 不存在并发

        //条件成立：说明当前线程持有的lock锁 已经完全释放了..
        if (c==0){
            //需要 1.exclusiveOwnerThread设置为null
            //2.设置state==0
            this.exclusiveOwnerThread=null;
            this.state=c;
            return true;
        }
        this.state=c;
        return false;
    }

    private static final Unsafe unsafe;
    private static final long stateOffset;
    private static final long headOffset;
    private static final long tailOffset;

    static {
        try {
            Field f = Unsafe.class.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            unsafe = (Unsafe) f.get(null);

            stateOffset = unsafe.objectFieldOffset
                    (MiniReentrantLock.class.getDeclaredField("state"));
            headOffset = unsafe.objectFieldOffset
                    (MiniReentrantLock.class.getDeclaredField("head"));
            tailOffset = unsafe.objectFieldOffset
                    (MiniReentrantLock.class.getDeclaredField("tail"));

        } catch (Exception ex) { throw new Error(ex); }
    }

    private final boolean compareAndSetHead(Node update) {
        return unsafe.compareAndSwapObject(this, headOffset, null, update);
    }

    private final boolean compareAndSetTail(Node expect,Node update) {
        return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
    }

    protected final boolean compareAndSetState(int expect, int update) {
        // See below for intrinsics setup to support this
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }
}
